#include "parameters.h"
#include "constants.h"
#include "utility.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>

// global variables, default values
bool gIsPeriodic = false;
int gNbeads = 1;
long int gNsteps = 500;
long int gNstepsWrite = 10;
double gTemperature = 100.0 ; // in K
double gBathParameter = 1; // in au
double gTimeStep = 1; // in au 时间步长（）fs
double gBoxLength = 24.0 * constants::Unit_Angstrom2au;  // Ar, in au
bool gIsVelocRead = false;
bool gIsCoordRead = false;
int gNatoms = 256;
std::string gAtomName = "Ar";
std::string gMolFileRead = "in.xyz";
std::string gMolFileWrite = "out.xyz";

DynamicsTypes gDynamicsType = DynamicsTypes::kMd;
EnsembleTypes gEnsembleType = EnsembleTypes::kNve;
BathMethods gBathMethod = BathMethods::kLangevin;
BaroMethods gBaroMethod = BaroMethods::kBerendsenBaro;
NveMethods gNveMethod = NveMethods::kLeapFrog;
NvtMethods gNvtMethod = NvtMethods::kLeapFrogMiddle;
NptMethods gNptMethod = NptMethods::kLeapFrogNpt;

// @TODO put force type and parameters in another file?
ForceTypes gForceType = ForceTypes::kLennardJones;
double gLjEpsilon = 0.9966288 * constants::Unit_kJpermol2au; // Ar, in au
double gLjSigma = 3.405 * constants::Unit_Angstrom2au;       // Ar, in au

// read parameters from file
void ReadParameters(const std::string &filename)
{
    std::ifstream infile(filename, std::ios::in);
    if (!infile)
    {
        std::cerr << "Error: file '" << filename << "' does NOT exist" << std::endl;
        exit(1);
    }
    std::string line;
    while (std::getline(infile, line))
    {
        if (line[0] == '#') // comment line
        {
            continue;
        }
        if (line == "") // blank line
        {
            continue;
        }
        std::vector<std::string> fields = Split(line, "=");
        if (fields.size() == 1)
        {
            continue;
        }
        for (size_t i = 0; i < fields.size(); i++)
        {
            fields[i] = Trim(fields[i]);
        }
        // below are possible variables
        if (fields[0] == "dynamics")
        {
            if (fields[1] == "MD")
            {
                gDynamicsType = DynamicsTypes::kMd;
            }
            else if (fields[1] == "PIMD")
            {
                gDynamicsType = DynamicsTypes::kPimd;
            }
            else if (fields[1] == "LSCIVR")
            {
                gDynamicsType = DynamicsTypes::kLscivr;
            }
        }
        if (fields[0] == "ensemble")
        {
            if (fields[1] == "NVE")
            {
                gEnsembleType = EnsembleTypes::kNve;
            }
            else if (fields[1] == "NVT")
            {
                gEnsembleType = EnsembleTypes::kNvt;
            }
            else if (fields[1] == "NPT")
            {
                gEnsembleType = EnsembleTypes::kNpt;
            }
        }
        if (fields[0] == "bath")
        {
            if (fields[1] == "Langevin")
            {
                gBathMethod = BathMethods::kLangevin;
            }
            else if (fields[1] == "Andersen")
            {
                gBathMethod = BathMethods::kAndersen;
            }
            else if (fields[1] == "NHC")
            {
                gBathMethod = BathMethods::kNoseHooverChain;
            }
            else if (fields[1] == "Berendsen")
            {
                gBathMethod = BathMethods::kBerendsen;
            }
        }
        if (fields[0] == "baro")
        {
            if (fields[1] == "Andersen")
            {
                gBaroMethod = BaroMethods::kAndersenBaro;
            }
            else if (fields[1] == "NHC")
            {
                gBaroMethod = BaroMethods::kNHCBaro;
            }
            else if (fields[1] == "Berendsen")
            {
                gBaroMethod = BaroMethods::kBerendsenBaro;
            }
            else
            {
                std::cerr <<"baro is not valid" <<std::endl;
                exit(1);
            }
        }
        if (fields[0] == "nsteps")
        {
            long val = std::stol(fields[1]);
            if (val <= 0)
            {
                std::cerr <<"nsteps is not valid" <<std::endl;
                exit(1);
            }
            gNsteps = val;
        }
        if (fields[0] == "nsteps_write")
        {
            long val = std::stol(fields[1]);
            if (val <= 0 || val >= gNsteps)
            {
                std::cerr << "nsteps_write is not valid" <<std::endl;
                exit(1);
            }
            gNstepsWrite = val;
        }
        if (fields[0] == "T(K)")
        {
            gTemperature = std::stod(fields[1]);
        }
        if (fields[0] == "nbeads")
        {
            gNbeads = std::stoi(fields[1]);
        }
        if (fields[0] == "dt(fs)")
        {
            gTimeStep = std::stod(fields[1]) * constants::Unit_fs2au;
        }
        if (fields[0] == "bath_parameter(1/ps)")
        {
            gBathParameter = std::stod(fields[1]) * 1.0E3 / constants::Unit_fs2au;
        }
        if (fields[0] == "is_periodic")
        {
            std::istringstream(fields[1]) >> std::boolalpha >> gIsPeriodic;
        }
        if (fields[0] == "force")
        {
            if (fields[1] == "LJ")
            {
                gForceType = ForceTypes::kLennardJones;
            }
        }
        if (fields[0] == "lj_sigma(AA)")
        {
            gLjSigma = std::stod(fields[1]) * constants::Unit_Angstrom2au;
        }
        if (fields[0] == "lj_epsilon(kJ/mol)")
        {
            gLjEpsilon = std::stod(fields[1]) * constants::Unit_kJpermol2au;
        }
        if (fields[0] == "mol_file_read")
        {
            gMolFileRead = fields[1];
        }
        if (fields[0] == "mol_file_write")
        {
            gMolFileWrite = fields[1];
        }
        if (fields[0] == "is_veloc_read")
        {
            std::istringstream(fields[1]) >> std::boolalpha >> gIsVelocRead;
        }
        if (fields[0] == "is_coord_read")
        {
            std::istringstream(fields[1]) >> std::boolalpha >> gIsCoordRead;
        }
        if (fields[0] == "atom_name")
        {
            gAtomName = fields[1];
        }
        if (fields[0] == "natoms")
        {
            gNatoms = std::stoi(fields[1]);
        }
    }
    return;
}

/*
 *  // 从界面读取以下数据
 * gNsteps = ->lineEdit1->text().toDouble();;
 * // 把数据写进参数文件
 *   WriteParameters("parameters.txt");
 */

void WriteParameters(const std::string& filename)
{
    std::ofstream outfile(filename);
    // nsteps
    outfile << "nsteps =" <<gNsteps << std::endl;
    // temperature
    outfile << "T(K) = " <<gTemperature <<std::endl;
    outfile << "nstepswrite = " << gNstepsWrite <<std::endl;
    //
    std::vector<std::string> force_type_str = {"LJ"};
    outfile << "force = " << force_type_str[gForceType] <<std::endl;
    //
std::vector<std::string> ensemble_type_str = {"NVE", "NVT", "NPT"};
    outfile << "ensemble = " << ensemble_type_str[gEnsembleType] <<std::endl;
 //@TODO   
}
